﻿// VirtualComputer.cs
// This program contains a class that simulates a Computer with a CPU.

using System;

namespace armsim
{
    /// <summary>
    /// Simulates an arm based computer and contains a virtualCPU
    /// </summary>
    public class VirtualComputer
    {
        /// <summary>
        /// Flag indicating whether a run has been cancelled or not
        /// </summary>
        private Boolean _cancelled;

        /// <summary>
        /// Flag indicating whether a run has been finished or not
        /// </summary>
        private Boolean _finished;

        /// <summary>
        /// Contains the file path of the current program in memory
        /// </summary>
        private String _currentFilePath;

        /// <summary>
        /// A virtual cpu that contains both ram and registers
        /// </summary>
        public VirtualCPU CPU { get; set; }

        /// <summary>
        /// Allows the computer to trace lines to a log file
        /// </summary>
        public Tracer Tracer { get; set; }

        /// <summary>
        /// Initialized a new VirtualComputer
        /// </summary>
        public VirtualComputer()
        {
            CPU = new VirtualCPU();
            //Tracer = new Tracer();
        }

        /// <summary>
        /// Initialized a new VirtualComputer with a given memory size
        /// </summary>
        /// <param name="ramSize">the number of bytes that the RAM will have</param>
        public VirtualComputer(uint ramSize)
        {
            CPU = new VirtualCPU(ramSize);
            //Tracer = new Tracer();
        }

        /// <summary>
        /// Gets the current file path
        /// </summary>
        /// <returns>string containing the current file path</returns>
        public String GetCurrentFilePath()
        {
            return _currentFilePath;
        }

        /// <summary>
        /// Sets the current file path
        /// </summary>
        /// <param name="path">path to be to set to</param>
        public void SetCurrentFilePath(String path)
        {
            _currentFilePath = path;

        }

        /// <summary>
        /// Resets the RAM, Registers, and File Path. Loads an elf program into memory.
        /// Updates the value of the PC to the entry point.
        /// </summary>
        /// <param name="fileName">elf file to be loaded into memory</param>
        /// <returns>the value of the entry point (the current value of the PC)</returns>
        public uint OpenNewProgram(String fileName)
        {
            // reset RAM
            CPU.RAM.ResetAll();

            // reset Registers
            CPU.Registers.ResetAll();

            // Set the current file path
            SetCurrentFilePath(fileName);

            // Reset the trace count
            // Tracer.CloseTraceFile();

            if (Tracer != null)
            {
                Tracer.CloseTraceFile();
            }
            Tracer = new Tracer();
            Tracer.OpenTraceFile();

            

            _cancelled = false;
            _finished = false;
            

            // load memory into RAM and get entry point
            var entryPoint = VirtualLoader.Load(fileName, CPU.RAM);

            // Set the PC to the entry point
            CPU.Registers.WriteWord(VirtualMemory.PC, entryPoint);

            // Set the stack pointer to 0x7000
            CPU.Registers.WriteWord(VirtualMemory.SP, 0x7000u);

            CPU.LastFetchedInstruction = CPU.RAM.ReadWord(entryPoint);

            CPU.CurrentInstruction = new BaseInstruction(0);

            // Return the entry point so that the GUI can do something with it
            return entryPoint;

        }

        /// <summary>
        /// Continuously executes fetch, decode, and execute cycles until
        /// cancelled or until a 0 value is fetched.
        /// </summary>
        public void Run()
        {
            _cancelled = false;
            while (!_finished && !_cancelled || CPU.CurrentInstruction is SwiInstruction)
            {
                var instructionWord = CPU.Fetch();

                if (instructionWord == 0u)
                {
                    _finished = true;
                    Tracer.CloseTraceFile();
                    return;
                }

                CPU.Decode();
                CPU.Execute();
                Tracer.Trace(CPU.RAM, CPU.Registers);
                
            }

        }

        /// <summary>
        /// Runs a single fetch, decode, execute cycle. 
        /// </summary>
        public Boolean Step()
        {
            if (!_finished)
            {
                var instructionWord = CPU.Fetch();
                
                if (0u == instructionWord || CPU.CurrentInstruction is SwiInstruction)
                {
                    _finished = true;
                    //Tracer.Trace(CPU.RAM, CPU.Registers);
                    Tracer.CloseTraceFile();
                    return false;
                }
                CPU.Decode();
                CPU.Execute();
                Tracer.Trace(CPU.RAM, CPU.Registers);
                return true;
            }

            return false;
        }

        /// <summary>
        /// Cancels the execution of the current running program.
        /// </summary>
        public void Cancel()
        {
            _cancelled = true;
        }

        /// <summary>
        /// Checks if the Computer is finished running a program or has been canceled
        /// </summary>
        /// <returns>True if finished or canceled. Otherwise, false.</returns>
        public Boolean IsFinishedOrCanceled()
        {

            return _finished || _cancelled;
        }

        /// <summary>
        /// Checks if the computer is finished running a program
        /// </summary>
        /// <returns>true if finished. Otherwise, false</returns>
        public Boolean IsFinished()
        {
            return _finished;
        }

        /// <summary>
        /// Checks if the computer program has been cancelled
        /// </summary>
        /// <returns>true if cancelled. Otherwise, false</returns>
        public Boolean IsCanceled()
        {
            return _cancelled;
        }

        


    }
}
